namespace GomuLibrary.IO
{
    using GomuLibrary.IO.DiscImage;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Threading;

    public sealed class SimpleIso9660Reader : Iso9660Reader, IDisposable
    {
        private uint _rootDirExtLoc;
        private uint _rootDirLength;
        private Thread _thExtraction;

        public event EventHandler<Iso9660FileExtractEventArgs> Aborted;

        public event EventHandler Terminate;

        public void Abort()
        {
            base.AutoThreadEvent.Set();
        }

        public void Close()
        {
            base.CloseDiscImageFile();
        }

        public void ExtractFile(string filePath, string output)
        {
            this.ExtractFile(filePath, output, false);
        }

        public void ExtractFile(string filePath, string output, bool newThread)
        {
            try
            {
                object[] parameter = new object[] { DiscImageReadMode.EXTRACT_FILE, 0, filePath, output };
                if (newThread)
                {
                    RecordEntryInfo info = base._basicFilesInfo[filePath.StartsWith(@"\") ? filePath : (@"\" + filePath)];
                    if (!string.IsNullOrEmpty(info.Name))
                    {
                        base.AutoThreadEvent.Reset();
                        new Thread(new ParameterizedThreadStart(this.ThExtractFile)) { Name = "ExtractFileMethod" }.Start(parameter);
                    }
                }
                else
                {
                    this.ThExtractFile(parameter);
                }
            }
            catch (ArgumentNullException exception)
            {
                throw new ArgumentNullException(exception.ParamName, exception.Message);
            }
            catch (ArgumentException exception2)
            {
                throw new ArgumentException(exception2.Message, exception2.ParamName);
            }
            catch (KeyNotFoundException)
            {
                throw new FileNotFoundException($"The file "+filePath+" does not exists !.");
            }
            catch (Exception)
            {
                throw new Exception("An internal error occured during the extraction process.");
            }
        }

        public void ExtractFullImage(string output)
        {
            this.ExtractFullImage(output, false);
        }

        public void ExtractFullImage(string output, bool newThread)
        {
            object[] parameter = new object[] { DiscImageReadMode.EXTRACT_FULL_IMAGE, output };
            try
            {
                if (newThread)
                {
                    if ((this._thExtraction == null) || (this._thExtraction.ThreadState != ThreadState.Running))
                    {
                        base.AutoThreadEvent.Reset();
                        if (!Directory.Exists(output))
                        {
                            Directory.CreateDirectory(output);
                        }
                        if (new DriveInfo(Path.GetPathRoot(output)).AvailableFreeSpace <= new FileInfo(base.DiscFilename).Length)
                        {
                            throw new IOException("Not enough disk space for extracting the full disc image.");
                        }
                        this._thExtraction = new Thread(new ParameterizedThreadStart(this.ThExtractDiscImage));
                        this._thExtraction.Name = "ExtractDiscImageMethod";
                        this._thExtraction.Start(parameter);
                    }
                }
                else
                {
                    this.ThExtractDiscImage(parameter);
                }
            }
            catch (ArgumentNullException exception)
            {
                throw new ArgumentNullException(exception.ParamName, exception.Message);
            }
            catch (ArgumentException exception2)
            {
                throw new ArgumentException(exception2.Message, exception2.ParamName);
            }
            catch (Exception)
            {
                throw new Exception("An internal error occured during the extraction process.");
            }
        }

        private string[] FilterEntries(string path, bool directory, SearchOption recursive)
        {
            if (((path == @"\") && (recursive == SearchOption.AllDirectories)) && directory)
            {
                return this.GetTable();
            }
            Predicate<RecordEntryInfo> match = null;
            this.GetFileSystemEntries(path, recursive);
            List<string> lEntries = new List<string>();
            if (base._basicFilesInfo.Count > 0)
            {
                List<RecordEntryInfo> list = new List<RecordEntryInfo>(base._basicFilesInfo.Values);
                if (match == null)
                {
                    match = delegate (RecordEntryInfo rec) {
                        if (directory)
                        {
                            if (rec.Directory)
                            {
                                lEntries.Add(rec.FullPath);
                                return true;
                            }
                            return false;
                        }
                        if (rec.Directory)
                        {
                            return false;
                        }
                        lEntries.Add(rec.FullPath);
                        return true;
                    };
                }
                list.FindAll(match);
            }
            return lEntries.ToArray();
        }

        public string[] GetDirectories() => 
            this.FilterEntries(@"\", true, SearchOption.AllDirectories);

        public string[] GetDirectories(string path) => 
            this.FilterEntries(path, true, SearchOption.AllDirectories);

        public string[] GetDirectories(string path, SearchOption recursive) => 
            this.FilterEntries(path, true, recursive);

        public string[] GetFiles() => 
            this.FilterEntries(@"\", false, SearchOption.AllDirectories);

        public string[] GetFiles(string path) => 
            this.FilterEntries(path, false, SearchOption.AllDirectories);

        public string[] GetFiles(string path, SearchOption recursive) => 
            this.FilterEntries(path, false, recursive);

        public string[] GetFileSystemEntries() => 
            this.GetFileSystemEntries(@"\", SearchOption.AllDirectories);

        public string[] GetFileSystemEntries(string path) => 
            this.GetFileSystemEntries(path, SearchOption.AllDirectories);

        public string[] GetFileSystemEntries(string path, SearchOption recursive)
        {
            string[] strArray;
            try
            {
                this.GetTable();
                PathTableRecordPub pub = base._tableRecords[path.StartsWith(@"\") ? path : (@"\" + path)];
                base.BaseFileStream = new FileStream(base.DiscFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
                base.BaseBinaryReader = new BinaryReader(base.BaseFileStream);
                if (!string.IsNullOrEmpty(pub.Name))
                {
                    return base.GetFileSystemEntries(path, pub.ExtentLocation, this._rootDirLength, recursive == SearchOption.AllDirectories, true).ToArray();
                }
                strArray = null;
            }
            catch (ArgumentNullException exception)
            {
                throw new ArgumentNullException(exception.ParamName, exception.Message);
            }
            catch (ArgumentException exception2)
            {
                throw new ArgumentException(exception2.Message, exception2.ParamName);
            }
            catch (KeyNotFoundException)
            {
                throw new DirectoryNotFoundException($"The entry "+path+" does not exists in the paths table");
            }
            catch (Exception exception3)
            {
                throw new Exception("Method failed, an internal error occured.", exception3);
            }
            return strArray;
        }

        public string[] GetPathsTable() => 
            this.GetTable();

        protected override string[] GetTable()
        {
            this.Close();
            string[] table = base.GetTable();
            Array.Sort<string>(table);
            return table;
        }

        public VolumeInfo Open(string discImageFile) => 
            this.Open(discImageFile, ImageFileFormat.ISO);

        public VolumeInfo Open(string discImageFile, ImageFileFormat type)
        {
            this.Close();
            switch (type)
            {
                case ImageFileFormat.ISO:
                    base.DataBeginSector = 0;
                    base.SectorSize = 0x800;
                    break;

                case ImageFileFormat.BIN_Mode1:
                    base.DataBeginSector = 0x10;
                    base.SectorSize = 0x930;
                    break;

                case ImageFileFormat.BIN_Mode2_Form1:
                    base.DataBeginSector = 0x18;
                    base.SectorSize = 0x930;
                    break;

                case ImageFileFormat.BIN_Mode2_Form2:
                    base.DataBeginSector = 8;
                    base.SectorSize = 0x920;
                    break;

                case ImageFileFormat.MDF:
                    base.DataBeginSector = 0x10;
                    base.SectorSize = 0x930;
                    break;

                case ImageFileFormat.CCD_Mode1:
                    base.DataBeginSector = 0x10;
                    base.SectorSize = 0x930;
                    break;

                case ImageFileFormat.CCD_Mode2:
                    base.DataBeginSector = 0x18;
                    base.SectorSize = 0x930;
                    break;
            }
            return base.ReadVolumeDescriptor(discImageFile, ref this._rootDirExtLoc, ref this._rootDirLength);
        }

        private void ThExtractDiscImage(object param)
        {
            try
            {
                object[] objArray = (object[]) param;
                DiscImageReadMode mode = (DiscImageReadMode) objArray[0];
                string output = objArray[1].ToString();
                uint num = 0;
                base.BaseFileStream = new FileStream(base.DiscFilename, FileMode.Open, FileAccess.Read, FileShare.Read, base.SectorSize * 0x10);
                base.BaseBinaryReader = new BinaryReader(base.BaseFileStream);
                this.GetFileSystemEntries();
                lock (base._basicFilesInfo)
                {
                    Dictionary<string, RecordEntryInfo>.ValueCollection values = base._basicFilesInfo.Values;
                    foreach (RecordEntryInfo info in values)
                    {
                        if (base.AutoThreadEvent.WaitOne(0, false))
                        {
                            if (this.Aborted != null)
                            {
                                Iso9660FileExtractEventArgs e = new Iso9660FileExtractEventArgs(info.FullPath, 0, 0, 0, output, 0);
                                this.Aborted(null, e);
                                break;
                            }
                        }
                        else if (info.Directory)
                        {
                            string path = Path.Combine(output, info.FullPath.StartsWith(@"\") ? info.FullPath.Substring(1) : info.FullPath);
                            if (!Directory.Exists(path))
                            {
                                Directory.CreateDirectory(path);
                            }
                        }
                        else
                        {
                            this.ThExtractFile(new object[] { mode, num, info.FullPath, output });
                            num++;
                        }
                    }
                    if (this.Terminate != null)
                    {
                        this.Terminate(null, EventArgs.Empty);
                    }
                }
            }
            catch (IOException exception)
            {
                throw new IOException(exception.Message, exception.InnerException);
            }
            catch (Exception exception2)
            {
                throw new Exception("Method failed, an internal error occured.", exception2);
            }
            finally
            {
                this.Close();
            }
        }

        private void ThExtractFile(object param)
        {
            object[] objArray = (object[]) param;
            DiscImageReadMode mode = (DiscImageReadMode) objArray[0];
            int num = Convert.ToInt32(objArray[1]);
            string str = objArray[2].ToString();
            string outputPath = objArray[3].ToString();
            base.ReadFile(base._basicFilesInfo[str], outputPath, mode, 0, new int?(num));
            if (mode == DiscImageReadMode.EXTRACT_FILE)
            {
                this.Close();
                if (this.Terminate != null)
                {
                    this.Terminate(null, EventArgs.Empty);
                }
            }
        }
    }
}

